import Utils from "../utils/index";
import Dom from '../utils/Dom';
import Events from '../utils/Events';
import Config from '../config';

export default (H, Gantt) => {
   // 计算渐变色
   let each = H.each,
      defined = H.defined,
      pick = H.pick,
      grep = H.grep,
      fireEvent = H.fireEvent,
      arrayMin = H.arrayMin,
      arrayMax = H.arrayMax,
      isNumber = H.isNumber,
      objectEach = H.objectEach,
      AxisGetOffset = H.Axis.prototype.getOffset,
      colors = [
         ['#6C2391', '#8446A3', '#9B68B1', '#B38ACA', '#C6ACD0'],
         ['#23906B', '#45A381', '#69B39A', '#89C4B1', '#ACD5C8']
      ],
      statusIcons = { // 状态图标
         'I': { // 下 PDI 状态
            src: 'images/op-pdi-released.png',
            size: 10
         },
         'P': {
            src: 'images/op-processing.png',
            size: 11
         }
      };

   colors.forEach((c, index) => {
      let color = {
            linearGradient: {
               x1: 0,
               y1: 0,
               x2: 0,
               y2: 1
            },
            stops: []
         },
         i = 0,
         total = 11,
         cIndex = 0;
      for (; i < total; i++) {
         cIndex = i - 1;
         if (cIndex >= 5) {
            cIndex = 8 - cIndex;
         }
         if (cIndex < 0) {
            cIndex = 0;
         }
         color.stops.push([parseFloat((0.1 * i).toFixed(2)), c[cIndex]]);
      }
      colors[index] = color;
   });
   console.log(colors);

   let svgDefs = `<svg xmlns="http://www.w3.org/2000/svg" >
        <defs>
            <linearGradient x1="0" y1="0" x2="0" y2="1" id="hc-finished">
                <stop offset="0" stop-color="#959595" stop-opacity="1"></stop>
                <stop offset="0.1" stop-color="#A6A4A0" stop-opacity="1"></stop>
                <stop offset="0.2" stop-color="#B1B1B2" stop-opacity="1"></stop>
                <stop offset="0.3" stop-color="#C1C1C1" stop-opacity="1"></stop>
                <stop offset="0.4" stop-color="#D8D7DA" stop-opacity="1"></stop>
                <stop offset="0.5" stop-color="#D8D7DA" stop-opacity="1"></stop>
                <stop offset="0.6" stop-color="#D8D7DA" stop-opacity="1"></stop>
                <stop offset="0.7" stop-color="#C1C1C1" stop-opacity="1"></stop>
                <stop offset="0.8" stop-color="#B1B1B2" stop-opacity="1"></stop>
                <stop offset="0.9" stop-color="#A6A4A0" stop-opacity="1"></stop>
                <stop offset="1" stop-color="#959595" stop-opacity="1"></stop>
            </linearGradient>
            <linearGradient x1="0" y1="0" x2="0" y2="1" id="hc-selected">
                <stop offset="0" stop-color="#1818F7" stop-opacity="1"></stop>
                <stop offset="0.1" stop-color="#1E1DFF" stop-opacity="1"></stop>
                <stop offset="0.3" stop-color="#3837FF" stop-opacity="1"></stop>
                <stop offset="0.5" stop-color="#8D8DFF" stop-opacity="1"></stop>
                <stop offset="0.7" stop-color="#3837FF" stop-opacity="1"></stop>
                <stop offset="0.9" stop-color="#1E1DFF" stop-opacity="1"></stop>
                <stop offset="1" stop-color="#1818F7" stop-opacity="1"></stop>
            </linearGradient>
            <linearGradient x1="0" y1="0" x2="0" y2="1" id="hc-current">
                <stop offset="0" stop-color="#d5a94d" stop-opacity="1"></stop>
                <stop offset="0.2" stop-color="#dbb360" stop-opacity="1"></stop>
                <stop offset="0.4" stop-color="#ecd69d" stop-opacity="1"></stop>
                <stop offset="0.6" stop-color="#dbb360" stop-opacity="1"></stop>
                <stop offset="0.8" stop-color="#d5a94d" stop-opacity="1"></stop>
                <stop offset="1" stop-color="#d5a94d" stop-opacity="1"></stop>
            </linearGradient>
            <linearGradient x1="0" y1="0" x2="0" y2="1" id="hc-warning">
                <stop offset="0" stop-color="#DC1EC2" stop-opacity="1"></stop>
                <stop offset="0.15" stop-color="#DC1EC2" stop-opacity="0.85"></stop>
                <stop offset="0.35" stop-color="#DC1EC2" stop-opacity="0.7"></stop>
                <stop offset="0.5" stop-color="#DC1EC2" stop-opacity="0.5"></stop>
                <stop offset="0.65" stop-color="#DC1EC2" stop-opacity="0.7"></stop>
                <stop offset="0.85" stop-color="#DC1EC2" stop-opacity="0.85"></stop>
                <stop offset="1" stop-color="#DC1EC2" stop-opacity="1"></stop>
            </linearGradient>
            <linearGradient x1="0" y1="0" x2="0" y2="1" id="hc-error">
                <stop offset="0" stop-color="#DC1EC2" stop-opacity="1"></stop>
                <stop offset="0.15" stop-color="#DC1EC2" stop-opacity="0.85"></stop>
                <stop offset="0.35" stop-color="#DC1EC2" stop-opacity="0.7"></stop>
                <stop offset="0.5" stop-color="#DC1EC2" stop-opacity="0.5"></stop>
                <stop offset="0.65" stop-color="#DC1EC2" stop-opacity="0.7"></stop>
                <stop offset="0.85" stop-color="#DC1EC2" stop-opacity="0.85"></stop>
                <stop offset="1" stop-color="#DC1EC2" stop-opacity="1"></stop>
            </linearGradient>
        </defs></svg>`,

      svgContainer = document.createElement('div');
   svgContainer.className = 'svg-linear-defs';
   svgContainer.innerHTML = svgDefs;
   document.body.appendChild(svgContainer);
   H.svgDefs = svgContainer.childNodes[0];



   // 设置全局配置
   H.setOptions({
      global: {
         timezoneOffset: -8 * 60
      },
      lang: {
         thousandsSep: ',',
         noData: '暂无数据'
      },
      colors: colors
   });

   /**
    * 重写 Highcharts 甘特图绘制数据点方法，添加自定的图形部分，包括连接点及连接线、数据标签、图标等
    */
   H.wrap(H.seriesTypes.xrange.prototype, 'drawPoint', function (proceed) {
      proceed.apply(this, Array.prototype.slice.call(arguments, 1));

      var point = arguments[1],
         series = this,
         chart = series.chart,
         index = point.index,
         series = point.series,
         pointsLength = series.points.length - 1,
         p1 = [point.shapeArgs.x, point.shapeArgs.y],
         p2 = [p1[0] + point.shapeArgs.width, p1[1] + point.shapeArgs.height],
         p3 = null,
         graphic = point && point.graphic,
         renderer = graphic && graphic.renderer;
      if (!graphic || !renderer) {
         return false;
      }

      // 开始点
      if (index !== 0) {
         if (point.startPoint) {
            point.startPoint.destroy();

         }
         point.startPoint = drawPoint(p1, renderer, graphic);
         if (point.link) {
            point.link.destroy();
         }
         p3 = series.points[index - 1];

         // 连接线
         point.link = renderer.path([
               'M',
               p1[0], p1[1],
               'L',
               p3.shapeArgs.x + p3.shapeArgs.width, p3.shapeArgs.y + p3.shapeArgs.height
            ]).attr({
               'stroke-width': 1,
               zIndex: 2,
               'stroke': '#333'
            })
            .css({
               'pointer-events': 'none'
            })
            .add(graphic);

      }
      // 结束点
      if (index !== pointsLength) {
         if (point.endPoint) {
            point.endPoint.destroy();
         }
         point.endPoint = drawPoint(p2, renderer, graphic);
      }


      // 绘制状态标签
      if (point.statusIcon) {
         point.statusIcon.destroy();
         point.statusIcon = null;
      }

      let icon = statusIcons[point.extra.operationStat];
      if (icon) {
         point.statusIcon = renderer.image(icon.src, point.shapeArgs.x, point.shapeArgs.y, icon.size, icon.size)
            .attr({
               zIndex: 10
            })
            .add(graphic);
      }


      // 冲突图块
      if (point.xConflictEl) {
         point.xConflictEl.forEach(el => {
            el.destroy();
         });
         point.xConflictEl = null;
      }

      if (!chart.isDragging && point.xConflicted) {
         let xAxis = point.series.xAxis,
            x1 = null,
            x2 = null,
            key = null,
            el = null;
         point.xConflictEl = [];
         for (key in point.xConflicted) {
            x1 = xAxis.toPixels(point.xConflicted[key].time[0], true);
            x2 = xAxis.toPixels(point.xConflicted[key].time[1], true);

            el = renderer.rect(x1 + 1, point.shapeArgs.y + 1, x2 - x1 - 2, point.shapeArgs.height - 2)
               .attr({
                  'storke-width': 0
               })
               .add(graphic);

            el.addClass('highcharts-point-error');
            point.xConflictEl.push(el);
         }


      }

      if (point.rConflictEl) {
         point.rConflictEl.forEach(el => {
            el.destroy();
         });
         point.rConflictEl = null;
      }
      if (!chart.isDragging && point.rConflicted) {
         let xAxis = point.series.xAxis,
            x1 = null,
            x2 = null,
            el = null;
         point.rConflictEl = [];
         point.rConflicted.forEach(conflict => {
            x1 = xAxis.toPixels(conflict[0], true);
            x2 = xAxis.toPixels(conflict[1], true);

            el = renderer.rect(x1 + 1, point.shapeArgs.y + 1, x2 - x1 - 2, point.shapeArgs.height - 2)
               .attr({
                  'storke-width': 0
               })
               .add(graphic);

            el.addClass('highcharts-point-error');
            point.rConflictEl.push(el);
         });
      }

      // 数据标签
      if (point.myDataLabel) {
         point.myDataLabel.destroy();
         point.myDataLabel = null;
      }

      let text = H.dateFormat('%H:%M', point.x) + '/' + (point.series.options.taskCounter || ''),
         wordCount = null,
         x = point.shapeArgs.x,
         y = point.shapeArgs.y - 4;

      if (x < 0 && (point.shapeArgs.width + x) < point.shapeArgs.width / 2) {
         return false;
      } else {
         wordCount = Math.floor(point.shapeArgs.width / 6);
         if (wordCount < text.length) {
            text = text.substr(0, wordCount);
         }
      }

      point.myDataLabel = renderer.label(text, x, y)
         .attr({
            width: point.shapeArgs.width
         })
         .add(graphic);
   });


   /**
    * 给数据点添加状态类，用于根据状态显示不同的颜色
    */
   H.wrap(H.seriesTypes.xrange.prototype.pointClass.prototype, 'getClassName', function (proceed) {
      let className = proceed.apply(this, Array.prototype.slice.call(arguments, 1)),
         statusClassMap = {
            F: 'finished',
            //P: 'finished',
            C: 'cancle'
         },
         statusClass = statusClassMap[this.extra.operationStat];

      statusClass = statusClass ? (' hc-' + statusClass) : ''


      return className + statusClass;
   });


   // 全局变量
   let currentPoint = null;

   H.Chart.prototype.selectedChange = function () {
      removeCurrentSelectPoint(this);
      Gantt.tooltip.hide();
      let points = this.getSelectedPoints();
      if (points.length > 0) {
         points.forEach(p => {
            p.select(false);
         });
      }
      Events.emit('statusChange', 'nodeSelect', false);
   }


   // 重写 Point 的 setState 函数
   H.Point.prototype.updateConflictStates = function () {
      let point = this;

      if (
         (this.xConflicted && !Utils.isObjEmpty(this.xConflicted)) || // 图块冲突
         (this.sConflicted && !Utils.isObjEmpty(this.sConflicted)) || // 连线冲突 
         this.cConflicted || // 铸机冲突
         (this.rConflicted && this.rConflicted.length > 0) // 设备维修冲突
      ) {
         point.graphic.addClass('highcharts-point-warning');
      } else {
         point.graphic.removeClass('highcharts-point-warning');
      }
   }

   // 渲染检修时间背景色块
   H.Chart.prototype.renderRepairs = function () {
      if (!this.repairsElements) {
         this.repairsElements = {};
      }
      // 最终用于设备维修检验
      this.repairs = Gantt.timeRange.getCurrentFacilityRepairs();

      let sid = null;
      if (this.repairs) {
         for (sid in this.repairs) {
            addOrUpdateRepair(this.repairsElements[sid], this, sid, this.repairs[sid]);
         }
      } else {
         for (sid in this.repairsElements) {
            this.repairsElements[sid].destroy();
            delete this.repairsElements[sid];
         }
      }
   }

   function addOrUpdateRepair(element, chart, sid, repair) {
      let xAxis = chart.xAxis[0],
         yAxis = chart.yAxis[0],
         x = xAxis.toPixels(repair[1]),
         x2 = xAxis.toPixels(repair[2]),
         y = Utils.facilitiesNoMap[repair[0]],
         height = null;
      if (y === undefined) {
         return false;
      }
      y = yAxis.toPixels(y);

      if (element) {
         element.attr({
            x: x,
            width: x2 - x,
            y: y + 0.5
         });
      } else {
         height = yAxis.toPixels(1) - yAxis.toPixels(0);
         chart.repairsElements[sid] = chart.renderer.rect(x, y + 0.5, x2 - x, height)
            .attr({
               fill: 'rgba(0, 0, 0, 0.1)'
            }).add(chart.seriesGroup);
      }
   }


   // 重写获取坐标轴关联的数据极值，针对 Y 轴直接范围 0 ~ 设备数量
   H.Axis.prototype.getSeriesExtremes = function () {
      var axis = this,
         chart = axis.chart;

      fireEvent(this, 'getSeriesExtremes', null, function () {

         axis.hasVisibleSeries = false;

         // Reset properties in case we're redrawing (#3353)
         axis.dataMin = axis.dataMax = axis.threshold = null;
         axis.softThreshold = !axis.isXAxis;

         if (axis.buildStacks) {
            axis.buildStacks();
         }

         // loop through this axis' series
         each(axis.series, function (series) {

            if (series.visible || !chart.options.chart.ignoreHiddenSeries) {

               var seriesOptions = series.options,
                  xData,
                  threshold = seriesOptions.threshold,
                  seriesDataMin,
                  seriesDataMax;

               axis.hasVisibleSeries = true;

               // Validate threshold in logarithmic axes
               if (axis.positiveValuesOnly && threshold <= 0) {
                  threshold = null;
               }

               // Get dataMin and dataMax for X axes
               if (axis.isXAxis) {
                  xData = series.xData;
                  if (xData.length) {
                     // If xData contains values which is not numbers,
                     // then filter them out. To prevent performance hit,
                     // we only do this after we have already found
                     // seriesDataMin because in most cases all data is
                     // valid. #5234.
                     seriesDataMin = arrayMin(xData);
                     seriesDataMax = arrayMax(xData);

                     if (
                        !isNumber(seriesDataMin) &&
                        !(seriesDataMin instanceof Date) // #5010
                     ) {
                        xData = grep(xData, isNumber);
                        // Do it again with valid data
                        seriesDataMin = arrayMin(xData);
                        seriesDataMax = arrayMax(xData);
                     }

                     if (xData.length) {
                        axis.dataMin = Math.min(
                           pick(axis.dataMin, xData[0], seriesDataMin),
                           seriesDataMin
                        );
                        axis.dataMax = Math.max(
                           pick(axis.dataMax, xData[0], seriesDataMax),
                           seriesDataMax
                        );
                     }
                  }
               } else {
                  // y 轴直接范围 0 ~ facilities.length
                  axis.dataMin = 0;
                  axis.dataMax = Utils.facilities.length;

                  // Adjust to threshold
                  if (defined(threshold)) {
                     axis.threshold = threshold;
                  }
                  // If any series has a hard threshold, it takes
                  // precedence
                  if (
                     !seriesOptions.softThreshold ||
                     axis.positiveValuesOnly
                  ) {
                     axis.softThreshold = false;
                  }
               }
            }
         });
      });

      fireEvent(this, 'afterGetSeriesExtremes');
   }

   // 重写坐标轴计算大小函数，保证有滚动条的情况下滚动过程中，Y 轴所占的位置一致
   H.Axis.prototype.getOffset = function () {
      var axis = this,
         chart = axis.chart,
         renderer = chart.renderer,
         options = axis.options,
         tickPositions = axis.tickPositions,
         ticks = axis.ticks,
         horiz = axis.horiz,
         side = axis.side,
         invertedSide = chart.inverted &&
         !axis.isZAxis ? [1, 0, 3, 2][side] : side,
         hasData,
         showAxis,
         titleOffset = 0,
         titleOffsetOption,
         titleMargin = 0,
         axisTitleOptions = options.title,
         labelOptions = options.labels,
         labelOffset = 0, // reset
         labelOffsetPadded,
         axisOffset = chart.axisOffset,
         clipOffset = chart.clipOffset,
         clip,
         directionFactor = [-1, 1, 1, -1][side],
         className = options.className,
         axisParent = axis.axisParent, // Used in color axis
         lineHeightCorrection,
         tickSize;

      // For reuse in Axis.render
      hasData = axis.hasData();
      axis.showAxis = showAxis = hasData || pick(options.showEmpty, true);

      // Set/reset staggerLines
      axis.staggerLines = axis.horiz && labelOptions.staggerLines;

      // Create the axisGroup and gridGroup elements on first iteration
      if (!axis.axisGroup) {
         axis.gridGroup = renderer.g('grid')
            .attr({
               zIndex: options.gridZIndex || 1
            })
            .addClass(
               'highcharts-' + this.coll.toLowerCase() + '-grid ' +
               (className || '')
            )
            .add(axisParent);
         axis.axisGroup = renderer.g('axis')
            .attr({
               zIndex: options.zIndex || 2
            })
            .addClass(
               'highcharts-' + this.coll.toLowerCase() + ' ' +
               (className || '')
            )
            .add(axisParent);
         axis.labelGroup = renderer.g('axis-labels')
            .attr({
               zIndex: labelOptions.zIndex || 7
            })
            .addClass(
               'highcharts-' + axis.coll.toLowerCase() + '-labels ' +
               (className || '')
            )
            .add(axisParent);
      }

      if (hasData || axis.isLinked) {

         // Generate ticks
         each(tickPositions, function (pos, i) {
            // i is not used here, but may be used in overrides
            axis.generateTick(pos, i);
         });

         axis.renderUnsquish();


         // Left side must be align: right and right side must
         // have align: left for labels
         axis.reserveSpaceDefault = (
            side === 0 ||
            side === 2 || {
               1: 'left',
               3: 'right'
            } [side] === axis.labelAlign
         );
         if (pick(
               labelOptions.reserveSpace,
               axis.labelAlign === 'center' ? true : null,
               axis.reserveSpaceDefault)) {
            if(axis.col === 'yAxis') {
               labelOffset = Gantt.maxFacilitiesLabelWidth;
            } else {
               each(tickPositions, function (pos) {
                  // get the highest offset
                  labelOffset = Math.max(
                     ticks[pos].getLabelSize(),
                     labelOffset
                  );
               });
            }
         }

         if (axis.staggerLines) {
            labelOffset *= axis.staggerLines;
         }
         axis.labelOffset = labelOffset * (axis.opposite ? -1 : 1);

      } else { // doesn't have data
         objectEach(ticks, function (tick, n) {
            tick.destroy();
            delete ticks[n];
         });
      }

      if (
         axisTitleOptions &&
         axisTitleOptions.text &&
         axisTitleOptions.enabled !== false
      ) {
         axis.addTitle(showAxis);

         if (showAxis && axisTitleOptions.reserveSpace !== false) {
            axis.titleOffset = titleOffset =
               axis.axisTitle.getBBox()[horiz ? 'height' : 'width'];
            titleOffsetOption = axisTitleOptions.offset;
            titleMargin = defined(titleOffsetOption) ?
               0 :
               pick(axisTitleOptions.margin, horiz ? 5 : 10);
         }
      }

      // Render the axis line
      axis.renderLine();

      // handle automatic or user set offset
      axis.offset = directionFactor * pick(options.offset, axisOffset[side]);

      axis.tickRotCorr = axis.tickRotCorr || {
         x: 0,
         y: 0
      }; // polar
      if (side === 0) {
         lineHeightCorrection = -axis.labelMetrics().h;
      } else if (side === 2) {
         lineHeightCorrection = axis.tickRotCorr.y;
      } else {
         lineHeightCorrection = 0;
      }

      // Find the padded label offset
      labelOffsetPadded = Math.abs(labelOffset) + titleMargin;
      if (labelOffset) {
         labelOffsetPadded -= lineHeightCorrection;
         labelOffsetPadded += directionFactor * (
            horiz ?
            pick(
               labelOptions.y,
               axis.tickRotCorr.y + directionFactor * 8
            ) :
            labelOptions.x
         );
      }

      axis.axisTitleMargin = pick(titleOffsetOption, labelOffsetPadded);

      if (axis.getMaxLabelDimensions) {
         axis.maxLabelDimensions = axis.getMaxLabelDimensions(
            ticks,
            tickPositions
         );
      }

      // Due to GridAxis.tickSize, tickSize should be calculated after ticks
      // has rendered.
      tickSize = this.tickSize('tick');

      axisOffset[side] = Math.max(
         axisOffset[side],
         axis.axisTitleMargin + titleOffset + directionFactor * axis.offset,
         labelOffsetPadded, // #3027
         hasData && tickPositions.length && tickSize ?
         tickSize[0] + directionFactor * axis.offset :
         0 // #4866
      );

      // Decide the clipping needed to keep the graph inside
      // the plot area and axis lines
      clip = options.offset ?
         0 :
         Math.floor(axis.axisLine.strokeWidth() / 2) * 2; // #4308, #4371
      clipOffset[invertedSide] = Math.max(clipOffset[invertedSide], clip);

      fireEvent(this, 'afterGetOffset');
   }

   // 选中点击关联的点
   H.Chart.prototype.selectSeriesByPoint = function (point) {
      removeCurrentSelectPoint(this);
      point.graphic.element.classList.add('highcharts-point-current');
      point.series.group.toFront();
      this.currentSelectPoint = point;
      point.series.points.forEach((p, index) => {
         p.select(true, index);
      });
      Gantt.tooltip.show('simple', point);
      Gantt.tooltip.hide('detail');
      Events.emit('statusChange', 'nodeSelect', point);
   }

   H.Chart.prototype.searchSeriesByHeatSid = function (heatSid) {
      let i = 0,
         total = this.series.length,
         isSingle = typeof heatSid !== 'object',
         result = [];
      if (isSingle) {
         heatSid = [heatSid];
      }
      for (; i < total; i++) {
         if (heatSid.indexOf(this.series[i].options.heatSid) !== -1) {
            result.push(this.series[i]);
            if (isSingle) {
               return this.series[i];
            }
         }
      }
      return result.length ? result : null;
   }



   // 鼠标拖动，右键菜单相关事件处理
   H.Chart.prototype.callbacks.push(function (chart) {

      H.addEvent(chart.container, 'mousedown', function (e) {
         if (e.target.className && e.target.className.baseVal.indexOf('highcharts-scrollbar') !== -1) {
            return false;
         }
         e = chart.pointer.normalize(e);
         currentPoint = chart.pointer.getPointFromEvent(e) || null;
         chart.dragging = [e.chartX, e.chartY];
         if (currentPoint) {

            chart.selectSeriesByPoint(currentPoint);
            // 炉次交换操作
            if (Gantt.heatChangeSid && Gantt.heatChangeSid !== currentPoint.extra.heatSid) { // 是否是炉次交换操作
               if (Gantt.canHeatChange(currentPoint)) {
                  let heat = Gantt.cache.getById('heats', currentPoint.extra.heatSid);
                  Gantt.modal.heatChangeInput2.value = heat.taskCounter;
                  Gantt.heatChangeNewSid = heat.sid;
               }
               return false;
            }

         } else {
            chart.selectedChange();
         }
         return Dom.nodefault(e);
      });


      // 数据点右键菜单
      H.addEvent(chart.container, 'contextmenu', function (e) {
         if (Gantt.heatChangeSid) {
            return Dom.nodefault(e);
         }

         e = chart.pointer.normalize(e);
         currentPoint = chart.pointer.getPointFromEvent(e) || null;
         if (currentPoint) {
            // 判断是否是转炉工序，如果是，则请求数据并弹窗
            // 否则直接调用接口
            Gantt.tooltip.hide('detail');
            Gantt.contextmenu.showPointContextmenu(currentPoint, e);
         }
         chart.dragging = false;
         return Dom.nodefault(e);
      });

      //键盘上下左右键事件，执行图块上下左右移动，左右移动，一次移动10秒
      H.addEvent(document.body, 'keydown', function (e) {
         if (currentPoint) {
            if (currentPoint.extra&&currentPoint.extra.operationStat&&currentPoint.extra.operationStat !== 'S') {
               return Dom.nodefault(e);
            }
            if (Config.ifShowView) {
               return Dom.nodefault(e);
            }
            let code = e.keyCode;
            if (code === 37 || code === 38 || code === 39 || code === 40) {
               if (code === 38 || code === 40) {
                  let newValue = {
                     x: currentPoint.x,
                     x2: currentPoint.x2
                  };
                  let lane = Utils.calcLaneDrag(currentPoint.y),
                     facility = Utils.facilities[Math.floor(currentPoint.y)],
                     isDoubleStation = facility.facilityType === 'D',
                     changeRange = isDoubleStation ? 0.5 : 1,
                     newY = currentPoint.y + (code === 38 ? -changeRange : changeRange);
                  if (lane && lane.name!="SCM" && newY < lane.max && newY > lane.min) {
                     newValue.y = newY;
                     newValue.extra = currentPoint.extra;
                     newValue.extra.facilityNo = Utils.facilities[Math.floor(newY)].facilityNo;
                     if (isDoubleStation) {
                        newValue.extra.stationNo = Math.round(newY) > newY ? 2 : 1;
                     }
                     console.log(newValue.extra.stationNo);
                  }
                  currentPoint.update(newValue, true, false);
                  if (!Gantt.changedOperations[currentPoint.series.options.id]) {
                     Gantt.changedOperations[currentPoint.series.options.id] = {};
                  }
                  Gantt.changedOperations[currentPoint.series.options.id][currentPoint.extra.sid] = true;
               } else if (code === 37 || code === 39) {
                  let newValue = {
                     x: currentPoint.x + (code === 37 ? -1000 : 1000) * 10,
                     x2: currentPoint.x2 + (code === 37 ? -1000 : 1000) * 10,
                     y: currentPoint.y,
                     extra: currentPoint.extra
                  };
                  currentPoint.update(newValue, true, false);
                  if (!Gantt.changedOperations[currentPoint.series.options.id]) {
                     Gantt.changedOperations[currentPoint.series.options.id] = {};
                  }
                  Gantt.changedOperations[currentPoint.series.options.id][currentPoint.extra.sid] = true;
               }
               currentPoint['simpleTooltipHTML'] = null;
               Gantt.tooltip.show('simple', currentPoint, true);
               Events.emit('statusChange', 'dataUpdate', true);
               Gantt.timer.addTimeout('validatePoints', 100, () => {
                  Gantt.validate.validate(false);
               });
            }
         }
      });

      // 鼠标拖动事件，执行画布拖动或数据点拖动
      H.addEvent(document.body, 'mousemove', function (e) {
         if (!chart.dragging || Gantt.heatChangeSid) {
            return false;
         }

         chart.isDragging = true;
         e = chart.pointer.normalize(e);
         let xAxis = chart.xAxis[0],
            yAxis = chart.yAxis[0],
            range = Math.round(xAxis.toValue(e.chartX) - xAxis.toValue(chart.dragging[0])),
            yRange = yAxis.toValue(e.chartY) - yAxis.toValue(chart.dragging[1]),
            newValue = null;

         if (currentPoint) {
            if (currentPoint.extra.operationStat !== 'S') {
               return Dom.nodefault(e);
            }
            if (Config.ifShowView) {
               return Dom.nodefault(e);
            }
            if (Gantt.dragModel === 'single') {
               newValue = {
                  x: currentPoint.x + range,
                  x2: currentPoint.x2 + range
               };
               if (Math.abs(yRange) > 0.05) {
                  // TODO: 检查 calcLaneDrag 是否有误，目前某些情况下返回 null
                  // 双工位处理
                  let lane = Utils.calcLaneDrag(currentPoint.y),
                     facility = Utils.facilities[Math.floor(currentPoint.y)],
                     isDoubleStation = facility.facilityType === 'D',
                     changeRange = isDoubleStation ? 0.5 : 1,
                     newY = currentPoint.y + (yRange < 0 ? -changeRange : changeRange);

                  if (lane && lane.name!="SCM" && newY < lane.max && newY > lane.min) {
                     newValue.y = newY;
                     newValue.extra = currentPoint.extra;
                     newValue.extra.facilityNo = Utils.facilities[Math.floor(newY)].facilityNo;
                     if (isDoubleStation) {
                        newValue.extra.stationNo = Math.round(newY) > newY ? 2 : 1;
                     }
                     console.log(newValue.extra.stationNo);
                  }
               }
               currentPoint.update(newValue, true, false);


               if (!Gantt.changedOperations[currentPoint.series.options.id]) {
                  Gantt.changedOperations[currentPoint.series.options.id] = {};
               }

               /* changedOperations 格式 {
                   "121": { // operationRoute Sid
                       12: true // operation sid
                   }
               }*/
               Gantt.changedOperations[currentPoint.series.options.id][currentPoint.extra.sid] = true;

            } else {
               // 浇次模式，通过 heatSid 找到 heat， 然后找到相同浇次的所有炉次
               if (Gantt.dragModel === 'castGroup') {
                  let heats = Gantt.cache.getHeatsByHeatSid(currentPoint.extra.heatSid),
                     heatSids = [],
                     targetSeries = [];
                  heats.forEach(h => {
                     heatSids.push(h.sid);
                  });

                  currentPoint.series.chart.series.forEach(s => {
                     if (heatSids.indexOf(s.options.heatSid) !== -1) {
                        targetSeries.push(s);
                     }
                  });

                  groupDragging(targetSeries, range, currentPoint.series.chart);

               } else {
                  groupDragging([currentPoint.series], range, currentPoint.series.chart);
               }

            }
            currentPoint['simpleTooltipHTML'] = null;
            Gantt.tooltip.show('simple', currentPoint, true);
            Events.emit('statusChange', 'dataUpdate', true);
            Gantt.timer.addTimeout('validatePoints', 100, () => {
               Gantt.validate.validate(false);
            });
         } else {
            Gantt.tooltip.hide('simple');
            Gantt.currentTimeRange = [xAxis.min - range, xAxis.max - range];
            xAxis.setExtremes(Gantt.currentTimeRange[0], Gantt.currentTimeRange[1], true, false);
            chart.renderRepairs();
         }
         Gantt.tooltip.hide('detail');
         chart.dragging = [e.chartX, e.chartY];
         return Dom.nodefault(e);
      });

      H.addEvent(document.body, 'mouseup', function (e) {
         // currentPoint = null;
         chart.dragging = false;
         chart.isDragging = false;
      });
   });



   // 重写滚动条定位方法，使其定位更精准
   H.wrap(H.Scrollbar.prototype, 'position', function (proceed) {
      proceed.apply(this, Array.prototype.slice.call(arguments, 1));

      this.group.attr({
         translateX: this.x - 1.5
      });

      let height = this.height,
         yOffset = this.yOffset,
         vertical = this.options.vertical;

      this.track.attr({
         height: height - 1
      });

      this.scrollbarButtons[1].attr({
         translateY: vertical ? height - yOffset - 1 : 0
      });

      if (!this.borderLine) {
         let path = ['M', this.width, -1, 'L', this.width, height];
         this.borderLine = this.renderer.path(path)
            .attr({
               stroke: '#7A7A7A',
               'stroke-width': 1
            }).add(this.group);
      }
   })

   // 组模式和浇次模式拖动
   function groupDragging(series, range, chart) {
      series.forEach(s => {
         if (!Gantt.changedOperations[s.options.id]) {
            Gantt.changedOperations[s.options.id] = {};
         }
         s.points.forEach(p => {
            if (p.extra.operationStat !== 'S') {
               return;
            }
            p.update({
               x: p.x + range,
               x2: p.x2 + range
            }, false);
            Gantt.changedOperations[s.options.id][p.extra.sid] = true;
         });
      });

      chart.redraw();
   }

   function removeCurrentSelectPoint(chart) {
      if (chart.currentSelectPoint) {
         if (chart.currentSelectPoint.graphic) {
            chart.currentSelectPoint.graphic.element.classList.remove('highcharts-point-current');
         }
         chart.currentSelectPoint = null;
      }
   }

   function drawPoint(point, renderer, graphic) {
      return renderer.circle(point[0], point[1], 2).attr({
         'fill': '#03f704',
         'stroke': '#333',
         'stroke-width': 1,
         zIndex: 4
      }).add(graphic);
   }
};